﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Net;
using System.Text.RegularExpressions;
using System.Threading;
using System.IO;
using System.Diagnostics;


namespace Ssrtr
{
    /// <summary>
    ///  Main class of SSRTR
    /// </summary>
    class Program
    {
        static UdpClient udp;
        static IPEndPoint udp_ep;
        static string xIp = "192.168.1.1";
        static string xPort = "5060";
        static bool xDetailRtp = false;
        static bool xDetailSip = false;
        static bool xOutputRDP = false;
        static bool xOutputTXT = false;
        static string xCodec = "9";
        static bool xECHO = false;
        static bool xChangePortRTP = false;
        static string xPath = "d:\\";
        static int counter = 1000;
        //Dictionary to know running threads
        static Dictionary<string, ReceiveRDP> runningThreads = new Dictionary<string, ReceiveRDP>();

        /// <summary>
        /// Main Thread (Startup)
        /// No args needed
        /// </summary>
        static void Main(string[] args)
        {
            // Aplication version and credits
            Console.WriteLine("SsRtr v1.0 - Simple SIP RTP trafic receiver");
            Console.WriteLine("Created By: João Jarreta 2014, License: GNU General Public License, ver.2");
            Console.WriteLine("-----------------------------------------------------------------------------");
            Console.WriteLine();

            // Initial parameters to run the aplication

            string line;
            IPAddress[] localIPs = Dns.GetHostAddresses(Dns.GetHostName());

            // Detect first IPv4 IP Address
            foreach (IPAddress ip in localIPs)
            {
                if (ip.AddressFamily == AddressFamily.InterNetwork)              
                {
                    xIp = ip.ToString();
                    break;
                }
            }
            
            // Set Ip Adress
            Console.Write("Enter the host IP Address, press Enter for default (" + xIp + "): ");
            line = Console.ReadLine();
            if (line != "") xIp = line; 
            // Set Port
            Console.Write("Enter the Port Number, press Enter for default (" + xPort + "): ");
            line = Console.ReadLine();
            if (line != "") xPort = line;
            // Set Detail on or off
            Console.Write("Show full detail Sip (y/n), press Enter for default (n): ");
            line = Console.ReadLine();
            if (line == "y") xDetailSip = true;
            // Set Detail on or off
            Console.Write("Show full detail Rdp (y/n), press Enter for default (n): ");
            line = Console.ReadLine();
            if (line == "y") xDetailRtp = true;
            // Set output RDP to file on or off
            Console.Write("Write RDP Stream to file (y/n), press Enter for default (n): ");
            line = Console.ReadLine();
            if (line == "y") xOutputRDP = true;
            // Set output TXT to file
            Console.Write("Write results to text file (y/n), press Enter for default (n): ");
            line = Console.ReadLine();
            if (line == "y") xOutputTXT = true;
            // Set path for the output TXT and RDP files
            if (xOutputRDP || xOutputTXT)
            {
                Console.Write("Output files Path, press Enter for default (d:\\): ");

                line = Console.ReadLine();
                if (line != "") xPath = line;
            }
            // Set Echo response on or OFF;
            Console.Write("Reply RTP codec (0=PCMU,3=GSM,8=PCMA), press Enter for default (9=AUTO): ");
            line = Console.ReadLine();
            if (line != "9") xCodec = line;
            // Set Echo response on or OFF;
            Console.Write("Echo RTP stream (y/n), press Enter for default (n): ");
            line = Console.ReadLine();
            if (line == "y") xECHO = true;
            if (xECHO)
            {
                Console.Write("Change RTP output port, press Enter for default (n): ");

                line = Console.ReadLine();
                if (line != "") xChangePortRTP = true;
            }
            // Clear questions in console
            Console.Clear();

            //Create endpoint
            udp_ep = new IPEndPoint(IPAddress.Any, 5060);

            //Initialize UdpClient
            udp = new UdpClient(udp_ep);

            //Begin waiting for a response asynchronously
            udp.BeginReceive(new AsyncCallback(UDP_IncomingData), udp_ep);

            Console.WriteLine("SsRtr ready for receiving data...");
            Console.WriteLine();

            //Prevent the console from closing automatically
            Console.ReadKey();
            Console.WriteLine("Exit...");
        }

        /// <summary>
        ///  Receiving thread for UDP socket receiving 
        /// </summary>
        /// <param name="ar"></param>
        static void UDP_IncomingData(IAsyncResult ar)
        {

            byte[] finalExpression = new byte[] { 0x20 };

            //Get the data from the response
            byte[] data = udp.EndReceive(ar, ref udp_ep);

            string ipRemoto = udp_ep.Address.ToString();

            counter++;


            if (xDetailSip) Console.WriteLine("\n\r" + System.Text.ASCIIEncoding.ASCII.GetString(data, 0, data.Length) + "\n\r");

            string[] response = Sip.getResponse(System.Text.ASCIIEncoding.ASCII.GetString(data, 0, data.Length), xCodec, xIp, counter, xChangePortRTP);

            string msgID = response[0];


           if (response[2].Equals("2")) // response to INVITE
           {

               Console.WriteLine(msgID + " -> Start (" + ipRemoto + ")");

               finalExpression = Encoding.ASCII.GetBytes(response[3]);

               // Thread variables to recieve RTP in the port received in the INVITE.
               int threadPortRDP = Convert.ToInt32(response[1]);
               string threadID = msgID;

               ReceiveRDP rRDP = new ReceiveRDP(threadID, threadPortRDP, xDetailRtp, xOutputRDP, xOutputTXT, xECHO, xPath);

               //Add(athread.Name, athread);
               runningThreads.Add(msgID, rRDP);

               //Send reply 180 Ringing
               udp.Send(finalExpression, finalExpression.Length, udp_ep);

               // Start thread to recive RTP in the port received in the INVITE.
               Thread thread = new Thread(unused => rRDP.startRDP());
               thread.IsBackground = true;
               thread.Name = string.Format(msgID);
               thread.Start();

               if (xDetailSip) Console.WriteLine("\n\r" + response[3]);

   
               // Encode the message header and message body in one stream
               finalExpression = Encoding.ASCII.GetBytes(response[4]);

               //Send the data
               udp.Send(finalExpression, finalExpression.Length, udp_ep);

               if (xDetailSip) Console.WriteLine("\n\r" + response[4]);
           }

           if (response[2].Equals("1"))  // response to BYE
           {
                // Encode the message header and message body in one stream
                finalExpression = Encoding.ASCII.GetBytes(response[3]);
                //Send the data
                udp.Send(finalExpression, finalExpression.Length, udp_ep);

                if (xDetailSip) Console.WriteLine("\n\r" + response[3]);

                // Stop thread and leave;
                stopThread(msgID);

                Console.WriteLine(msgID + " -> End   (" + ipRemoto + ")");  
           }
           // Recursive Call to receive again
           udp.BeginReceive(new AsyncCallback(UDP_IncomingData), udp_ep);
        }

        /// <summary>
        /// Stop the rtp receiving thread (for one port)
        /// </summary>
        /// <param name="id">Thread id</param>
        private static void stopThread(string id)
        {

            ReceiveRDP th1 = runningThreads[id];
            //only when you are using dictionary
            th1.stopRDP();
         
            runningThreads.Remove(id);
        }
      
        /// <summary>
        /// Modify string to bytes
        /// </summary>
        /// <param name="str">string</param>
        /// <returns></returns>
        static byte[] GetBytes(string str)
        {
            byte[] bytes = new byte[str.Length * sizeof(char)];
            System.Buffer.BlockCopy(str.ToCharArray(), 0, bytes, 0, bytes.Length);
            return bytes;
        }

        /// <summary>
        /// Modify bytes to string
        /// </summary>
        /// <param name="bytes">bytes</param>
        /// <returns></returns>
        static string GetString(byte[] bytes)
        {
            char[] chars = new char[bytes.Length / sizeof(char)];
            System.Buffer.BlockCopy(bytes, 0, chars, 0, bytes.Length);
            return new string(chars);
        }
    }

}
